package com.netx.generics.time;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
import com.netx.generics.util.Strings;
import com.netx.generics.basic.Checker;
import com.netx.generics.basic.IntegrityException;
import com.netx.generics.time.TimeValue.MEASURE;



public class Time implements Formattable, Comparable<Time> {

	public static Time valueOf(String s) {
		return valueOf(s, DateFormat.getTimeInstance());
	}

	public static Time valueOf(String s, DateFormat df) {
		Checker.checkEmpty(s, "s");
		Checker.checkNull(df, "df");
		try {
			return new Moment(df.parse(s).getTime()).getTime();
		}
		catch(ParseException pe) {
			throw new DateFormatException("unparseable time: \""+s+"\"");
		}
	}

	private int _hours;
	private int _minutes;
	private int _seconds;
	private int _milliseconds;

	public Time() {
		this(Calendar.getInstance());
	}

	public Time(int hours, int minutes, int seconds, int milliseconds) {
		if(_hours<0 || _hours>23 || _minutes<0 || _minutes>59 || _seconds<0 || _seconds>59 || _milliseconds<0 || _milliseconds>999) {
			_throwIllegalArgument(hours, minutes, seconds, milliseconds);
		}
		_hours = hours;
		_minutes = minutes;
		_seconds = seconds;
		_milliseconds = milliseconds;
	}

	public Time(int hours, int minutes, int seconds) {
		this(hours, minutes, seconds, 0);
	}

	// for Moment.getTime
	Time(Calendar c) {
		_hours = c.get(Calendar.HOUR_OF_DAY);
		_minutes = c.get(Calendar.MINUTE);
		_seconds = c.get(Calendar.SECOND);
		_milliseconds = c.get(Calendar.MILLISECOND);
	}

	public int getHours() {
		return _hours;
	}
	
	public Time setHours(int hours) {
		if(hours<0 || hours>23) {
			_throwIllegalArgument(hours, _minutes, _seconds, _milliseconds);
		}
		_hours = hours;
 		return this;
	}
	
	public int getMinutes() {
		return _minutes;
	}
	
	public Time setMinutes(int minutes) {
		if(minutes<0 || minutes>59) {
			_throwIllegalArgument(_hours, minutes, _seconds, _milliseconds);
		}
		_minutes = minutes;
 		return this;
	}

	public int getSeconds() {
		return _seconds;
	}
	
	public Time setSeconds(int seconds) {
		if(seconds<0 || seconds>59) {
			_throwIllegalArgument(_hours, _minutes, seconds, _milliseconds);
		}
		_seconds = seconds;
 		return this;
	}

	public int getMilliseconds() {
		return _milliseconds;
	}
	
	public Time setMilliseconds(int milliseconds) {
		if(milliseconds<0 || milliseconds>999) {
			_throwIllegalArgument(_hours, _minutes, _seconds, milliseconds);
		}
		_milliseconds = milliseconds;
 		return this;
	}

	public String format() {
		return format(DateFormat.getTimeInstance());
	}

	public String format(DateFormat df) {
		Checker.checkNull(df, "df");
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(0);
		cal.set(Calendar.HOUR_OF_DAY, _hours);
		cal.set(Calendar.MINUTE, _minutes);
		cal.set(Calendar.SECOND, _seconds);
		cal.set(Calendar.MILLISECOND, _milliseconds);
		return df.format(cal.getTime());		
	}

	public String toString() {
		return format();
	}
	
	public int compareTo(Time t) {
		Checker.checkNull(t, "o");
		long result = new TimeValue(this).getAs(MEASURE.MILLISECONDS) - new TimeValue(t).getAs(MEASURE.MILLISECONDS);
		return result<0 ? -1 : 1;
	}
	
 	public boolean equals(Object o) {
 		if(o == this) {
 			return true;
 		}
 		if(o == null) {
 			return false;
 		}
 		if(o instanceof Time) {
 	 		return compareTo((Time)o)==0;
 		}
 		return false;
 	}
	
	public boolean after(Time t) {
		Checker.checkNull(t, "t");
		return compareTo(t) > 0;
	}

	public boolean before(Time t) {
		Checker.checkNull(t, "t");
		return compareTo(t) < 0;
	}
	
	public Time clone() {
		try {
			return (Time)super.clone();
		}
		catch(CloneNotSupportedException cnse) {
			throw new IntegrityException(cnse);
		}
	}
	
	private void _throwIllegalArgument(int hours, int minutes, int seconds, int millis) {
		throw new IllegalArgumentException("invalid Time value: "+Strings.valueOf(hours, 2)+":"+Strings.valueOf(minutes, 2)+":"+Strings.valueOf(seconds, 2)+":"+Strings.valueOf(millis, 4));
	}
}
